A deck as a sequence of cards


In [100]:
import collections

In [101]:
Card = collections.namedtuple('Card', ['rank', 'suit'])

In [102]:
class FrenchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()
    
    def __init__(self):
        self._cards = [Card(rank, suit) for suit in self.suits
                                        for rank in self.ranks]
        
    def __len__(self):
        return len(self._cards)
    
    def __getitem__(self, position):
        return self._cards[position]

In [103]:
beer_card = Card('7', 'diamonds')

In [104]:
beer_card


Out[104]:
Card(rank='7', suit='diamonds')

Deck responds to the len() function


In [105]:
deck = FrenchDeck()

In [106]:
len(deck)


Out[106]:
52

Reading specific cards from the deck is provided by the getitem method


In [107]:
deck[0]


Out[107]:
Card(rank='2', suit='spades')

In [108]:
deck[-1]


Out[108]:
Card(rank='A', suit='hearts')

Get a random item from a sequence: random.choice


In [109]:
from random import choice

In [110]:
choice(deck)


Out[110]:
Card(rank='8', suit='spades')

In [111]:
choice(deck)


Out[111]:
Card(rank='5', suit='spades')

In [112]:
choice(deck)


Out[112]:
Card(rank='8', suit='spades')

Deck supports slicing: getitem delegates to the [] operator

Top three cards in a brand new deck


In [113]:
deck[:3]


Out[113]:
[Card(rank='2', suit='spades'),
 Card(rank='3', suit='spades'),
 Card(rank='4', suit='spades')]

In [114]:
for idx, card in enumerate(deck):
    print(idx, ' -> ', card)


0  ->  Card(rank='2', suit='spades')
1  ->  Card(rank='3', suit='spades')
2  ->  Card(rank='4', suit='spades')
3  ->  Card(rank='5', suit='spades')
4  ->  Card(rank='6', suit='spades')
5  ->  Card(rank='7', suit='spades')
6  ->  Card(rank='8', suit='spades')
7  ->  Card(rank='9', suit='spades')
8  ->  Card(rank='10', suit='spades')
9  ->  Card(rank='J', suit='spades')
10  ->  Card(rank='Q', suit='spades')
11  ->  Card(rank='K', suit='spades')
12  ->  Card(rank='A', suit='spades')
13  ->  Card(rank='2', suit='diamonds')
14  ->  Card(rank='3', suit='diamonds')
15  ->  Card(rank='4', suit='diamonds')
16  ->  Card(rank='5', suit='diamonds')
17  ->  Card(rank='6', suit='diamonds')
18  ->  Card(rank='7', suit='diamonds')
19  ->  Card(rank='8', suit='diamonds')
20  ->  Card(rank='9', suit='diamonds')
21  ->  Card(rank='10', suit='diamonds')
22  ->  Card(rank='J', suit='diamonds')
23  ->  Card(rank='Q', suit='diamonds')
24  ->  Card(rank='K', suit='diamonds')
25  ->  Card(rank='A', suit='diamonds')
26  ->  Card(rank='2', suit='clubs')
27  ->  Card(rank='3', suit='clubs')
28  ->  Card(rank='4', suit='clubs')
29  ->  Card(rank='5', suit='clubs')
30  ->  Card(rank='6', suit='clubs')
31  ->  Card(rank='7', suit='clubs')
32  ->  Card(rank='8', suit='clubs')
33  ->  Card(rank='9', suit='clubs')
34  ->  Card(rank='10', suit='clubs')
35  ->  Card(rank='J', suit='clubs')
36  ->  Card(rank='Q', suit='clubs')
37  ->  Card(rank='K', suit='clubs')
38  ->  Card(rank='A', suit='clubs')
39  ->  Card(rank='2', suit='hearts')
40  ->  Card(rank='3', suit='hearts')
41  ->  Card(rank='4', suit='hearts')
42  ->  Card(rank='5', suit='hearts')
43  ->  Card(rank='6', suit='hearts')
44  ->  Card(rank='7', suit='hearts')
45  ->  Card(rank='8', suit='hearts')
46  ->  Card(rank='9', suit='hearts')
47  ->  Card(rank='10', suit='hearts')
48  ->  Card(rank='J', suit='hearts')
49  ->  Card(rank='Q', suit='hearts')
50  ->  Card(rank='K', suit='hearts')
51  ->  Card(rank='A', suit='hearts')

In [115]:
deck[12::13]


Out[115]:
[Card(rank='A', suit='spades'),
 Card(rank='A', suit='diamonds'),
 Card(rank='A', suit='clubs'),
 Card(rank='A', suit='hearts')]

Deck is iterable: getitem special method


In [116]:
for card in deck:
    print(card)


Card(rank='2', suit='spades')
Card(rank='3', suit='spades')
Card(rank='4', suit='spades')
Card(rank='5', suit='spades')
Card(rank='6', suit='spades')
Card(rank='7', suit='spades')
Card(rank='8', suit='spades')
Card(rank='9', suit='spades')
Card(rank='10', suit='spades')
Card(rank='J', suit='spades')
Card(rank='Q', suit='spades')
Card(rank='K', suit='spades')
Card(rank='A', suit='spades')
Card(rank='2', suit='diamonds')
Card(rank='3', suit='diamonds')
Card(rank='4', suit='diamonds')
Card(rank='5', suit='diamonds')
Card(rank='6', suit='diamonds')
Card(rank='7', suit='diamonds')
Card(rank='8', suit='diamonds')
Card(rank='9', suit='diamonds')
Card(rank='10', suit='diamonds')
Card(rank='J', suit='diamonds')
Card(rank='Q', suit='diamonds')
Card(rank='K', suit='diamonds')
Card(rank='A', suit='diamonds')
Card(rank='2', suit='clubs')
Card(rank='3', suit='clubs')
Card(rank='4', suit='clubs')
Card(rank='5', suit='clubs')
Card(rank='6', suit='clubs')
Card(rank='7', suit='clubs')
Card(rank='8', suit='clubs')
Card(rank='9', suit='clubs')
Card(rank='10', suit='clubs')
Card(rank='J', suit='clubs')
Card(rank='Q', suit='clubs')
Card(rank='K', suit='clubs')
Card(rank='A', suit='clubs')
Card(rank='2', suit='hearts')
Card(rank='3', suit='hearts')
Card(rank='4', suit='hearts')
Card(rank='5', suit='hearts')
Card(rank='6', suit='hearts')
Card(rank='7', suit='hearts')
Card(rank='8', suit='hearts')
Card(rank='9', suit='hearts')
Card(rank='10', suit='hearts')
Card(rank='J', suit='hearts')
Card(rank='Q', suit='hearts')
Card(rank='K', suit='hearts')
Card(rank='A', suit='hearts')

Deck can also be iterated in reverse


In [117]:
for card in reversed(deck):
    print(card)


Card(rank='A', suit='hearts')
Card(rank='K', suit='hearts')
Card(rank='Q', suit='hearts')
Card(rank='J', suit='hearts')
Card(rank='10', suit='hearts')
Card(rank='9', suit='hearts')
Card(rank='8', suit='hearts')
Card(rank='7', suit='hearts')
Card(rank='6', suit='hearts')
Card(rank='5', suit='hearts')
Card(rank='4', suit='hearts')
Card(rank='3', suit='hearts')
Card(rank='2', suit='hearts')
Card(rank='A', suit='clubs')
Card(rank='K', suit='clubs')
Card(rank='Q', suit='clubs')
Card(rank='J', suit='clubs')
Card(rank='10', suit='clubs')
Card(rank='9', suit='clubs')
Card(rank='8', suit='clubs')
Card(rank='7', suit='clubs')
Card(rank='6', suit='clubs')
Card(rank='5', suit='clubs')
Card(rank='4', suit='clubs')
Card(rank='3', suit='clubs')
Card(rank='2', suit='clubs')
Card(rank='A', suit='diamonds')
Card(rank='K', suit='diamonds')
Card(rank='Q', suit='diamonds')
Card(rank='J', suit='diamonds')
Card(rank='10', suit='diamonds')
Card(rank='9', suit='diamonds')
Card(rank='8', suit='diamonds')
Card(rank='7', suit='diamonds')
Card(rank='6', suit='diamonds')
Card(rank='5', suit='diamonds')
Card(rank='4', suit='diamonds')
Card(rank='3', suit='diamonds')
Card(rank='2', suit='diamonds')
Card(rank='A', suit='spades')
Card(rank='K', suit='spades')
Card(rank='Q', suit='spades')
Card(rank='J', suit='spades')
Card(rank='10', suit='spades')
Card(rank='9', suit='spades')
Card(rank='8', suit='spades')
Card(rank='7', suit='spades')
Card(rank='6', suit='spades')
Card(rank='5', suit='spades')
Card(rank='4', suit='spades')
Card(rank='3', suit='spades')
Card(rank='2', suit='spades')

in operator works because deck is iterable

if a collection does has no contains method, the in operator does a sequential scan


In [118]:
Card('Q', 'hearts') in deck


Out[118]:
True

In [119]:
Card('A', 'beasts') in deck


Out[119]:
False

Sorting: Ranking cards by rank


In [120]:
suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)

In [121]:
suit_values


Out[121]:
{'clubs': 0, 'diamonds': 1, 'hearts': 2, 'spades': 3}

In [122]:
def spades_high(card:Card):
    rank_value = FrenchDeck.ranks.index(card.rank)
    return rank_value * len(suit_values) + suit_values[card.suit]

In [123]:
spades_high(Card('2', 'clubs'))


Out[123]:
0

In [124]:
spades_high(Card('A', 'spades'))


Out[124]:
51

In [125]:
for card in sorted(deck, key=spades_high):
    print(card)


Card(rank='2', suit='clubs')
Card(rank='2', suit='diamonds')
Card(rank='2', suit='hearts')
Card(rank='2', suit='spades')
Card(rank='3', suit='clubs')
Card(rank='3', suit='diamonds')
Card(rank='3', suit='hearts')
Card(rank='3', suit='spades')
Card(rank='4', suit='clubs')
Card(rank='4', suit='diamonds')
Card(rank='4', suit='hearts')
Card(rank='4', suit='spades')
Card(rank='5', suit='clubs')
Card(rank='5', suit='diamonds')
Card(rank='5', suit='hearts')
Card(rank='5', suit='spades')
Card(rank='6', suit='clubs')
Card(rank='6', suit='diamonds')
Card(rank='6', suit='hearts')
Card(rank='6', suit='spades')
Card(rank='7', suit='clubs')
Card(rank='7', suit='diamonds')
Card(rank='7', suit='hearts')
Card(rank='7', suit='spades')
Card(rank='8', suit='clubs')
Card(rank='8', suit='diamonds')
Card(rank='8', suit='hearts')
Card(rank='8', suit='spades')
Card(rank='9', suit='clubs')
Card(rank='9', suit='diamonds')
Card(rank='9', suit='hearts')
Card(rank='9', suit='spades')
Card(rank='10', suit='clubs')
Card(rank='10', suit='diamonds')
Card(rank='10', suit='hearts')
Card(rank='10', suit='spades')
Card(rank='J', suit='clubs')
Card(rank='J', suit='diamonds')
Card(rank='J', suit='hearts')
Card(rank='J', suit='spades')
Card(rank='Q', suit='clubs')
Card(rank='Q', suit='diamonds')
Card(rank='Q', suit='hearts')
Card(rank='Q', suit='spades')
Card(rank='K', suit='clubs')
Card(rank='K', suit='diamonds')
Card(rank='K', suit='hearts')
Card(rank='K', suit='spades')
Card(rank='A', suit='clubs')
Card(rank='A', suit='diamonds')
Card(rank='A', suit='hearts')
Card(rank='A', suit='spades')

Composition: len and getitem implementation hand off all the work to list object, self._cards


In [ ]: